package com.linrung.sf.interviewitems;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.*;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.linrung.platform.job.jobConfig.JobConfigEntity;
import com.linrung.scheduler.core.AbstractJobBusinessService;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.*;

/**
 * 生成待走访清单
 * @author 545
 */
@Service("InterviewPopulationService")
public class InterviewPopulationService extends AbstractJobBusinessService {

    private static Logger logger = Logger.getLogger(InterviewPopulationService.class);

    @Autowired
    @Qualifier("jdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    private final String DEFAULT_REGION_SUFFIX = "4416";


    @Override
    public String execute(JobConfigEntity jc) {
        //0 0 2,5 ? * 1 ?         每周一凌晨2点检测生成数据
        //0 0 2,5 * * ?             每天2点或5点
        Map<String, String> config = jc.getParams();
        int pageSize = 5000;
        if(config.containsKey("PAGE_SIZE")){
            pageSize = Convert.toInt(config.get("PAGE_SIZE"));
        }

        try{
            logger.info("开始生成待访问清单.....");
            startGenPopulation(pageSize);
            logger.info("生成待访问清单结束.....");
        }catch (Exception e){
            logger.error("获取待访问清单发生错误：", e);
        }

        return null;
    }

    /**
     * 产生重点人员待访清单
     * @param pageSize
     */
    private void startGenPopulation(int pageSize){

        Date now = DateUtil.date();
        Date dayStartDate = DateUtil.beginOfDay(now);
        Date dayEndDate = DateUtil.endOfDay(now);
        Date weekStartDate = DateUtil.beginOfWeek(now).toJdkDate();
        Date weekEndDate = DateUtil.endOfWeek(now).toJdkDate();
        Date monthStartDate = DateUtil.beginOfMonth(now).toJdkDate();
        Date monthEndDate = DateUtil.endOfMonth(now).toJdkDate();
        Date quarterStartDate = DateUtil.beginOfQuarter(now).toJdkDate();
        Date quarterEndDate = DateUtil.endOfQuarter(now).toJdkDate();

        //检测本周是否已有生成数据
        String testSql = "SELECT COUNT(*) FROM LR_SF_INTERVIEW_TODO_ITEM T WHERE T.TODO_TYPE = 1 AND T.START_DATE >= ? AND T.END_DATE <= ?";
        boolean isGenWeekData = false;
        long count = this.jdbcTemplate.queryForObject(testSql, new Object[]{weekStartDate, weekEndDate}, Long.class);
        if(count > 0){
            isGenWeekData = true;   //本周生成
        }
        //检测本月是否生成
        boolean isGenMonthData = false;
        count = this.jdbcTemplate.queryForObject(testSql, new Object[]{monthStartDate, monthEndDate}, Long.class);
        if(count > 0){
            isGenMonthData = true;  //本月已生成
        }
        //检测本季是否生成
        boolean isGenQuarterData = false;
        count = this.jdbcTemplate.queryForObject(testSql, new Object[]{quarterStartDate, quarterEndDate}, Long.class);
        if(count > 0){
            isGenQuarterData = true;
        }
        //检测本日是否生成
        boolean isGenDayData = false;
        count = this.jdbcTemplate.queryForObject(testSql, new Object[]{dayStartDate, dayEndDate}, Long.class);
        if(count > 0){
            isGenDayData = true;
        }
        if(!isGenDayData){
            //进入这里，说明没有生成【日】待走访人员，所以先判断今天是不是假日
            String configSql = "select * from LR_SV_WORKDATE where work_Time >= ? and work_Time <= ?";
            List<Map<String, Object>> dateList = this.jdbcTemplate.queryForList(configSql, dayStartDate, dayEndDate);
            if(!DateUtil.isWeekend(now) && dateList.isEmpty()){
                //说明今天是正常工作日，需要生成待走访人员
//                isGenDayData = false;
            }else if(!dateList.isEmpty()){
                Map<String, Object> first = dateList.get(0);
                String workType = (String) first.get("WORK_TYPE");
                if(StrUtil.equals(workType, "1")){
                    //调休日，不需要生成
                    isGenDayData = true;
                }
            }
        }

        String dictSql = "select t.ITEM_CODE from LR_PF_DATADIC_ITEM t where t.ITEM_NAME like '%noCheck'";
        List<Map<String, Object>> dictList = this.jdbcTemplate.queryForList(dictSql);
        int[] rootNoCheckType = null;
        if(dictList.isEmpty()){
            rootNoCheckType = new int[]{10,11,15,16,17}; //不是正确配置的字典，这些都是根字典
        }else{
            rootNoCheckType = dictList.stream()
                    .map(map -> (String) map.get("ITEM_CODE"))
                    .mapToInt(Integer::valueOf)
                    .toArray();
        }
        try {
            logger.info("重点人员nocheck字典项：" + Arrays.toString(rootNoCheckType));
        }catch (Exception lError){
            logger.error(lError);
        }

        //注意这里，其实【日】待走访清单人员，每天都需要生成，而下面的语句是查所有的人员进遍历，这样处理对服务器也是要消耗不少性能，
        //所以如果只需要查找【日】的重要人员，可以考虑单独处理先筛选出【日】重点人员的类型，然后进行人员表进行条件筛选，先确定是否
        //存在【日】待走访人员，若存在，则全部查找出来遍历，不过这对于gbase而言，性能可能差不了多少，gbase对于like查找速度会慢很多
        if(!isGenWeekData || !isGenMonthData || !isGenQuarterData || !isGenDayData){
            String totalSql = "SELECT COUNT(*) FROM LR_DGS_POPULATION WHERE IS_IMPORTANT = 1 AND DATA_STATE = 1 AND POP_STATUS = 1";
            long totalCount = this.jdbcTemplate.queryForObject(totalSql, Long.class);

            long i=0;
            do{
                List<Map<String, Object>> list = loadPopulationList(i, pageSize, isGenMonthData, isGenQuarterData, isGenWeekData, isGenDayData, rootNoCheckType);
                genTodoItems(list, 1);

                i += pageSize;
            }while(i<totalCount);
        }else{
            logger.info("最后检查是否存在没有生成待走访清单的重要人员.....");
            List<Map<String, Object>> list = loadChangePopulationList(rootNoCheckType);
            genTodoItems(list, 1);
        }
    }



    public void genTodoItems(List<Map<String, Object>> list, int todoType) {
        if(list.isEmpty()){
            return;
        }

        String insert = "insert into LR_SF_INTERVIEW_TODO_ITEM(ID,TODO_TYPE,ITEM_ID,ITEM_TYPE,TODO_PERIOD,START_DATE,END_DATE,TODO_STATE,OVER_STATE,HANDLE_DATE,HANDLE_USER_ID,HANDLE_ORG_ID,HANDLE_DEPT_ID,HANDLE_USER_NAME,HANDLE_ORG_NAME,HANDLE_DEPT_NAME,TODO_COUNT,HADDO_COUNT,ITEM_NAME,ITEM_MOBILE,ITEM_ADDRESS,ITEM_LIVE_ID,INTERVIEW_ID,ITEM_GRID_ID,ITEM_REGION_CODE,ITEM_CER_NUM) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";

        Date now = DateUtil.date();
        Timestamp dayStartDate = DateUtil.beginOfDay(now).toTimestamp();
        Timestamp dayEndDate = DateUtil.endOfDay(now).toTimestamp();
        Timestamp weekStartDate = DateUtil.beginOfWeek(now).toTimestamp();
        Timestamp weekEndDate = DateUtil.endOfWeek(now).toTimestamp();
        Timestamp monthStartDate = DateUtil.beginOfMonth(now).toTimestamp();
        Timestamp monthEndDate = DateUtil.endOfMonth(now).toTimestamp();
        Timestamp quarterStartDate = DateUtil.beginOfQuarter(now).toTimestamp();
        Timestamp quarterEndDate = DateUtil.endOfQuarter(now).toTimestamp();
        jdbcTemplate.batchUpdate(insert, new BatchPreparedStatementSetter() {
            @Override
            public int getBatchSize() {
                return list.size();
            }
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                Map<String, Object> data = list.get(i);
                ps.setString(1, IdUtil.simpleUUID());
                ps.setInt(2, todoType);
                ps.setString(3, (String) data.getOrDefault("ID", null));
                ps.setInt(4, (Integer) data.getOrDefault("ITEM_TYPE", null));

                Integer itemType = (Integer) data.getOrDefault("TODO_PERIOD", null);
                if(itemType != null){
                    ps.setInt(5, itemType);
                    if(itemType == 0){//周
                        ps.setTimestamp(6, weekStartDate);
                        ps.setTimestamp(7, weekEndDate);
                    }else if(itemType == 1){//月
                        ps.setTimestamp(6, monthStartDate);
                        ps.setTimestamp(7, monthEndDate);
                    }else if(itemType == 2){//季
                        ps.setTimestamp(6, quarterStartDate);
                        ps.setTimestamp(7, quarterEndDate);
                    }else if(itemType == -1){//日
                        ps.setTimestamp(6, dayStartDate);
                        ps.setTimestamp(7, dayEndDate);
                    }
                }else{
                    ps.setInt(5, 9);
                }

                ps.setInt(8, 0);
                ps.setInt(9, 0);
                ps.setDate(10, null);
                ps.setString(11, null);
                ps.setString(12, null);
                ps.setString(13, null);
                ps.setString(14, null);
                ps.setString(15, null);
                ps.setString(16, null);
                ps.setInt(17, 0);
                ps.setInt(18, 0);

                ps.setString(19, (String) data.getOrDefault("POP_NAME", null));
                ps.setString(20, (String) data.getOrDefault("POP_TEL", null));
                ps.setString(21, (String) data.getOrDefault("POP_LIVE_ADDRESS", null));
                ps.setString(22, (String) data.getOrDefault("POP_LIVE_ID", null));
                ps.setString(23, null);
                ps.setString(24, (String) data.getOrDefault("GRID_ID", null));
                ps.setString(25, (String) data.getOrDefault("DATA_REGION_CODE", null));
                ps.setString(26, (String) data.getOrDefault("POP_CER_NUM", null));
            }
        });
    }

    /**
     * 查找符合条件的待走访的重点人员
     * @param i
     * @param pageSize
     * @param isGenMonthData        判定本月是否已经生成
     * @param isGenQuarterData      判定本季是否已经生成
     * @return
     */
    public List<Map<String, Object>> loadPopulationList(long i, int pageSize, boolean isGenMonthData, boolean isGenQuarterData, boolean isGenWeekData, boolean isGenDayData, int[] rootNoCheckType){
        List<Map<String, Object>> result = new ArrayList<>();

        //1、人口表，ID,名称,人员类型，住址，电话，所住房屋表ID，网格ID，所在区划码
        String msgSql = "SELECT * FROM (select a.ID,a.POP_NAME,a.GRID_ID,POP_LIVE_ID,POP_LIVE_ADDRESS,a.IMPORTANT_TYPE,a.POP_TEL,a.DATA_REGION_CODE,a.POP_CER_NUM,ROWNUM as RN from LR_DGS_POPULATION a where a.IS_IMPORTANT = 1 AND a.DATA_STATE = 1 AND a.POP_STATUS = 1) where RN > ? AND RN <= ?";
        List<Map<String, Object>> populationList = this.jdbcTemplate.queryForList(msgSql, i, i+pageSize);
        for(Map<String, Object> user : populationList){
            String userImportantType = (String) user.getOrDefault("IMPORTANT_TYPE", null);
            if(StringUtils.isEmpty(userImportantType)){
                continue;
            }

            int[] types = getPopulationImportantItemType(userImportantType, rootNoCheckType);
            int minPeriod = types[0];
            int validType = types[1];

            user.put("ITEM_TYPE", validType);
            user.put("TODO_PERIOD", minPeriod);
            if(minPeriod == -1 && !isGenDayData){
                result.add(user);
            }else if(minPeriod == 0 && !isGenWeekData){
                result.add(user);
            }else if(minPeriod == 1 && !isGenMonthData){
                result.add(user);
            }else if(minPeriod == 2 && !isGenQuarterData){
                result.add(user);
            }
        }
        return result;
    }


    /**
     * 获取临时更新/添加的重要人员，生成数据
     * @return
     */
    public List<Map<String, Object>> loadChangePopulationList(int[] rootNoCheckType){
        List<Map<String, Object>> result = new ArrayList<>();
        Date now = DateUtil.date();

        //1、人口表，ID,名称,人员类型，住址，电话，所住房屋表ID，网格ID，所在区划码
        String msgSql = "select a.ID,a.POP_NAME,a.GRID_ID,POP_LIVE_ID,POP_LIVE_ADDRESS,a.IMPORTANT_TYPE,a.POP_TEL,a.DATA_REGION_CODE,a.POP_CER_NUM,ROWNUM as RN from " +
                "LR_DGS_POPULATION a where a.IS_IMPORTANT = 1 AND a.DATA_STATE = 1 AND a.POP_STATUS = 1 " +
                "AND a.ID NOT IN ( SELECT ITEM_ID FROM LR_SF_INTERVIEW_TODO_ITEM WHERE TODO_TYPE = 1 AND END_DATE > ?)";
        List<Map<String, Object>> populationList = this.jdbcTemplate.queryForList(msgSql, new Object[]{now});
        if(populationList.isEmpty()){
            return new ArrayList<>();
        }

        for(Map<String, Object> user : populationList){
            String userImportantType = (String) user.getOrDefault("IMPORTANT_TYPE", null);
            if(StringUtils.isEmpty(userImportantType)){
                continue;
            }

            int[] types = getPopulationImportantItemType(userImportantType, rootNoCheckType);
            int minPeriod = types[0];
            int validType = types[1];

            user.put("ITEM_TYPE", validType);
            user.put("TODO_PERIOD", minPeriod);
            if(minPeriod == -1){
                result.add(user);
            }else if(minPeriod == 0){
                result.add(user);
            }else if(minPeriod == 1){
                result.add(user);
            }else if(minPeriod == 2){
                result.add(user);
            }
        }
        return result;
    }


    /**
     *  取重点人员对应的处理周期，默认情况给季度，如果是nocheck根字典值（无效错误值）那就默认给0，
     *  如果重点人员有多种类型，取最小的数值作为类型
     * @return 整形数组，第1位处理周期, 第2位是是重点人员类型，
     */
    private int[] getPopulationImportantItemType(String importantTypes, int[] rootNoCheckType){
        if(StrUtil.isBlank(importantTypes)){
            return new int[]{2, -1};
        }

        /*String configSql = "select * from LR_SF_WORKITEM_CONFIG";
        List<Map<String, Object>> configList = this.jdbcTemplate.queryForList(configSql);
*/

        List<Map<String, Object>> configList = this.listPreferredWorkItem();
        Map<String, Integer> itemTypesMap = new HashMap<>();
        for(Map<String, Object> item : configList){
            String dictCode = (String) item.getOrDefault("DICT_CODE", "");
            if(Objects.equals(dictCode,"DGS_POINT_TYPE_ST")){
                //重点人员
                String dictValue = (String) item.getOrDefault("DICT_VALUE", null);
                if(dictValue != null){
                    BigDecimal bigDecimal = (BigDecimal) item.get("ITEM_TYPE");
                    itemTypesMap.put(dictValue, bigDecimal.intValue());
                }
            }
        }

        //若走访人有多个重点人员特性，则取按走访时间最短的类型
        String[] userTypeArray = importantTypes.split(",");
        int minPeriod = 5;                          //其它
        String importantType = userTypeArray[0];
        for(String type : userTypeArray){
            if(Arrays.stream(rootNoCheckType).anyMatch(value -> value == Integer.parseInt(type))){
                if(userTypeArray.length == 1){
                    importantType = type;
                    minPeriod = 2;    //如果用户只有一个类型，且是不正确的类型，则直接分配给1季度走访一次
                    break;
                }
                continue;
            }
            if(!itemTypesMap.containsKey(type)){
                continue;
            }
            Integer period = itemTypesMap.get(type);
            if(period == null){
                continue;
            }
            if(period < minPeriod){
                minPeriod = period;
                importantType = type;
            }
            if(minPeriod == -1){
                break;
            }
        }
        if(minPeriod == 5){
            minPeriod = 2;  //其它情况给季
        }

        return new int[]{minPeriod, Integer.parseInt(importantType)};
    }


    /**
     *
     * @param regionCode
     * @param dictCode
     * @param dataType
     * @return
     */
    public List<Map<String, Object>> listWorkItem(String regionCode, String dictCode, String dataType) {
        String configSql = "select * from LR_SF_WORKITEM_CONFIG where REGION_CODE = ? and DATA_TYPE = ?";
        String defaultRegion = regionCode.substring(0, 4) + "0";
        List<Map<String, Object>> defaultList = this.jdbcTemplate.queryForList(configSql, defaultRegion, "DGS_POINT_TYPE_ST");

        List<Map<String, Object>> currentRegionList = null;
        if(StringUtils.isNotEmpty(regionCode) && !regionCode.equals(defaultRegion)){
            currentRegionList = this.jdbcTemplate.queryForList(configSql, regionCode, "DGS_POINT_TYPE_ST");
        }

        if(currentRegionList != null && !currentRegionList.isEmpty() && !defaultList.isEmpty()){
            for(Map<String, Object> currentRegionItem : currentRegionList){
                for(Map<String, Object> defaultRegionItem : defaultList){
                    if(Objects.equals(currentRegionItem.get("DICT_CODE"), defaultRegionItem.get("DICT_CODE"))
                            && Objects.equals(currentRegionItem.get("DICT_VALUE"), defaultRegionItem.get("DICT_VALUE"))){
                        BeanUtil.copyProperties(defaultRegionItem, currentRegionItem, true);
                    }
                }
            }
        }

        return defaultList;
    }

    public List<Map<String, Object>> listPreferredWorkItem() {
        //配置存储器
        List<Map<String, Object>> resultList = new ArrayList<>();
        //网格配置存储多key存储器，1orgId，2dictValue，3itemValue(网格最优先配置的值)
        Table<String, String, Integer> orgConfigTable = HashBasedTable.create();
        //网格配置存储多key存储器，1orgId，2dictValue，3itemType(网格最优先配置的周期)
        Table<String, String, Integer> orgConfigTypeTable = HashBasedTable.create();
        //保存各区划配置的值，1regionCode，2dictValue，3itemValue
        Table<String, String, Integer> regionCodeValueConfig = HashBasedTable.create();
        //保存各区划配置的周期，1regionCode，2dictValue，3itemType
        Table<String, String, Integer> regionCodeTypeConfig = HashBasedTable.create();

        //1、先第一步先获取所有配置，包括默认配置和特定的区划或机构配置
        String configSql = "select * from LR_SF_WORKITEM_CONFIG where REGION_CODE = ? and DATA_TYPE = ?";
        List<Map<String, Object>> allConfigListByDataType = this.jdbcTemplate.queryForList(configSql, DEFAULT_REGION_SUFFIX, "DGS_POINT_TYPE_ST");

        //先处理所有的配置
        for(Map<String, Object> workItemEntity : allConfigListByDataType){
            String itemRegionCode = (String) workItemEntity.get("REGION_CODE");
            if(itemRegionCode.equals(DEFAULT_REGION_SUFFIX)){
                //先把默认的配置存下来，到了后面会从网格开始往上筛选配置
                resultList.add(workItemEntity);
            }

            String itemOrgId = (String) workItemEntity.get("ORG_ID");
            String dictValue = (String) workItemEntity.get("DICT_VALUE");
            int itemType = Convert.toBigDecimal(workItemEntity.get("ITEM_TYPE"), BigDecimal.valueOf(2)).intValue();
            int itemValue = Convert.toBigDecimal(workItemEntity.get("ITEM_VALUE"), BigDecimal.valueOf(0)).intValue();
            if(StrUtil.isNotBlank(itemOrgId)){
                //这里不判断是否存在key了，数据库中必须唯一
                //即然配置了网格，那必定是最优先级别了，直接存下来
                orgConfigTable.put(itemOrgId, dictValue, itemValue);
                orgConfigTypeTable.put(itemOrgId, dictValue, itemType);
            }else{
                //保存区划的
                regionCodeValueConfig.put(itemRegionCode, dictValue, itemValue);
                regionCodeTypeConfig.put(itemRegionCode, dictValue, itemType);
            }
        }

        //2、获取当前区划下的所有网格
        String selectGridOrgList = "select * from LR_PF_ORG where REGIONCODE like '441602%' and ORG_WORK_TYPE = 5";
        List<Map<String, Object>> orgList = this.jdbcTemplate.queryForList(selectGridOrgList);

        //3、筛选每一个网格的不同类型的最优先配置。如：有网格的配置则配置网格，没有则往上取，先取村再镇街，以此类推，最终是默认配置44160
        for(Map<String, Object> workItemEntity : resultList){
            String dictValue = (String) workItemEntity.get("DICT_VALUE");
            int defaultValue = Convert.toBigDecimal(workItemEntity.get("ITEM_VALUE"), BigDecimal.valueOf(2)).intValue();
            //开始遍历处理大类下的子类
            int totalValue = 0;
            for(Map<String, Object> org : orgList){
                String orgId = (String) org.get("ORG_ID");
                String rCode = (String) org.get("REGIONCODE");

                //先找网格配置
                if(orgConfigTable.contains(orgId, dictValue)){
                    totalValue += orgConfigTable.get(orgId, dictValue);
                    workItemEntity.put("ITEM_TYPE", orgConfigTypeTable.get(orgId, dictValue));
                    continue;
                }

                //如果网格没找到，再找区域配置，先是村，再往上镇，县区，再到默认
                String tempCode = rCode;
                boolean isConfig = false;
                do{
                    if(regionCodeValueConfig.contains(tempCode, dictValue)){
                        //找到了最近的配置,取当前区划的配置（周期和数量值）
                        workItemEntity.put("ITEM_TYPE", regionCodeTypeConfig.get(tempCode, dictValue));
                        totalValue += regionCodeValueConfig.get(tempCode, dictValue);
                        isConfig = true;
                        break;
                    }
                    tempCode = tempCode.substring(0, tempCode.length() - 2);
                } while (tempCode.length() > DEFAULT_REGION_SUFFIX.length());


                if(!isConfig){
                    //取默认配置
                    totalValue += defaultValue;
                }
            }
            workItemEntity.put("AddItemValue", totalValue);
        }

        return resultList;
    }

}
